import { listYears } from "@/lib/storage"; import { getSession } from "@/lib/auth/session"; import { canAccessBranch } from "@/lib/auth/permissions"; import { withErrorHandling, json, badRequest, unauthorized, forbidden, } from "@/lib/api/errors"; import { mapStorageReadError } from "@/lib/api/storageErrors"; /** * Next.js Route Handler caching configuration (RHL-006): * * We force this route to execute dynamically on every request. * * Reasons: * - NAS contents can change at any time (new scans). * - Auth/RBAC-protected responses must not be cached/shared across users. * - We rely on a small storage-layer TTL micro-cache instead of Next route caching. */ export const dynamic = "force-dynamic"; /** * GET /api/branches/[branch]/years * * Happy-path response must remain unchanged: * { "branch": "NL01", "years": ["2024", ...] } */ export const GET = withErrorHandling( async function GET(request, ctx) { const session = await getSession(); if (!session) { throw unauthorized("AUTH_UNAUTHENTICATED", "Unauthorized"); } // Next.js App Router: params are resolved asynchronously. const { branch } = await ctx.params; // Validate required route params early (client error => 400). if (!branch) { throw badRequest( "VALIDATION_MISSING_PARAM", "Missing required route parameter(s)", { params: ["branch"] } ); } // RBAC: branch users can only access their own branch. if (!canAccessBranch(session, branch)) { throw forbidden("AUTH_FORBIDDEN_BRANCH", "Forbidden"); } try { const years = await listYears(branch); return json({ branch, years }, 200); } catch (err) { // Convert filesystem errors into: // - 404 if the requested path does not exist (but NAS root is reachable) // - 500 for system-level storage failures throw await mapStorageReadError(err, { details: { branch } }); } }, { logPrefix: "[api/branches/[branch]/years]" } );